package ntu.metaweb.storage;

import java.io.IOException;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import ntu.metaweb.XMLMarshaller;
import ntu.p2p.dht.DHTContent;
import ntu.p2p.dht.DHTContentKey;
import rice.p2p.commonapi.rawserialization.OutputBuffer;

public class WebPageQuery implements DHTContent {
	
	public static final short TYPE = 2;
	private static final long serialVersionUID = -1281208953892546096L;
	
	private WebPageQueryKey key;
	private URL url;
	private Date timestamp;
	private String title;
	// from URL to Link
	private HashMap<URL, Link> inLink;
	// to URL to Link
	private HashMap<URL, Link> outLink;
	private HashMap<String, Tag> tags;
	private HashMap<Link,Vector<String>> linkToUsers;
	private HashMap<String,Vector<Link>> userToLinks;
	private HashMap<String, Vector<Tag>> userToTags;
	private HashMap<String, FiveStarRating> rating;
	
	public WebPageQuery(WebPageQueryKey key, URL url, String title) {
		this.key = key;
		this.url = url;
		this.timestamp = Calendar.getInstance().getTime();
		this.title = title;
		this.inLink = new HashMap<URL, Link>();
		this.outLink = new HashMap<URL, Link>();
		this.tags = new HashMap<String, Tag>();
		this.linkToUsers = new HashMap<Link, Vector<String>>();
		this.userToLinks = new HashMap<String, Vector<Link>>();
		this.userToTags = new HashMap<String, Vector<Tag>>();
		this.rating = new HashMap<String, FiveStarRating>();
	}
	
	public URL getURL() {
		return this.url;
	}
	
	public String getTitle() {
		return this.title;
	}
	
	public Date getTimestamp() {
		return this.timestamp;
	}
	
	public Iterator<Tag> getTags() {
		return this.tags.values().iterator();
	}
	
	public void addLink(URL from, URL to, String user, String note) {
		if (from.equals(this.url)) {
			if (!this.outLink.containsKey(to)) {
				this.outLink.put(to, new Link(from, to));
			}
			this.outLink.get(to).addContributor(user, note);
		} else if (to.equals(this.url)) {
			if (!this.inLink.containsKey(from)) {
				this.inLink.put(from, new Link(from, to));
			}
			this.inLink.get(from).addContributor(user, note);
		}
	}
	
	public void rateLink(URL from, URL to, String user, int rating) {
		Link link = null;
		if (from.equals(this.url)) {
			link = this.outLink.get(to);
		} else {
			link = this.inLink.get(from);
		}

		if (link != null) {
			link.addRating(user, rating);
		}
	}
	
	public Iterator<Link> getInLinks() {
		return this.inLink.values().iterator();
	}
	
	public Iterator<Link> getOutLinks() {
		return this.outLink.values().iterator();
	}
	
	public Iterator<String> getUsersForTag(String tag) {
		if (this.tags.containsKey(tag)) {
			return this.tags.get(tag).contributors.iterator();
		}
		return null;
	}
	
	public Iterator<Tag> getTagsForUser(String user) {
		if (this.userToTags.containsKey(user)) {
			return this.userToTags.get(user).iterator();
		}
		return null;
	}
	
	@Override
	public int hashCode() {
		return this.url.hashCode();
	}
	
	@Override
	public boolean equals(Object o) {
		if (!(o instanceof WebPageQuery)) {
			return false;
		}
		WebPageQuery webPageQuery = (WebPageQuery) o;
		return this.url.equals(webPageQuery.url);
	}
	
	public void addRating(String rater, int rating) {
		if (this.rating.containsKey(rater)) {
			this.rating.get(rater).setRating(rating);
		} else {
			this.rating.put(rater, new FiveStarRating(rater, rating));
		}
	}
	
	public void addTag(String tag, String contributor) {
		System.out.println("add tag...");
		if (!this.tags.containsKey(tag)) {
			tags.put(tag, new Tag(tag, this.url));
		}
		this.tags.get(tag).addContributor(contributor);

	}
	
	public int getUserRating(String user) {
		return (this.rating.containsKey(user)) ? this.rating.get(user).getRating() : -1;
	}
	
	public int getTotalRating() {
		int total = 0;
		for (Rating r : this.rating.values()) {
			total += r.rating;
		}
		return total;
	}
	
	public double getAverageRating() {
		int total = this.getTotalRating();
		return (this.rating.values().size() == 0) ? 0 : (total / this.rating.size());
	}

	@Override
	public DHTContentKey getKey() {
		return this.key;
	}

	@Override
	public void serialize(OutputBuffer buf) throws IOException {
		String temp = XMLMarshaller.getInstance().marshall(this);
		buf.writeInt(temp.length());
		buf.write(temp.getBytes(), 0, temp.length());
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public void updateTagRating(String tag, String user, boolean up) {
		if (tags.containsKey(tag)) {
			tags.get(tag).addRating(user, up ? 1 : -1);
		}
	}
	
	public HashMap<URL,Link> getInLink() {
		return this.inLink;
	}
	
	public HashMap<URL,Link> getOutLink() {
		return this.outLink;
	}

	public void setLinkTitle(URL url, String title) {
		if (this.inLink.containsKey(url)) {
			Link temp = this.inLink.get(url);
			temp.setTitle(title);
		}
		if (this.outLink.containsKey(url)) {
			Link temp = this.outLink.get(url);
			temp.setTitle(title);
		}
	}

	public void updateLinkedPageRating(URL url, double averageRating) {
		if (this.inLink.containsKey(url)) {
			Link temp = this.inLink.get(url);
			temp.setPageRating(averageRating);
		}
		if (this.outLink.containsKey(url)) {
			Link temp = this.outLink.get(url);
			temp.setPageRating(averageRating);
		}
	}

	public void deleteRating(String tag, String tagger) {
		if (this.tags.containsKey(tag)) {
			this.tags.get(tag).getContributors().remove(tagger);
		}
	}

	public void deleteLink(URL from, URL to, String user) {
		if (from.equals(this.url)) {
			if (this.outLink.containsKey(to)) {
				this.outLink.get(to).deleteContributor(user);

			}
		} else if (to.equals(this.url)) {
			if (this.inLink.containsKey(from)) {
				this.inLink.get(from).deleteContributor(user);
			}
		}
	}
}
